package gr.unipi.mscd1;

import java.util.*;

import org.snmp4j.*;
import org.snmp4j.event.*;
import org.snmp4j.smi.*;

public abstract class SnmpInfo extends SnmpGenInfo implements ResponseListener {
	private int currentOIDItem;
	
	private boolean isInit;
	private boolean hasRepeatedValues;
	private boolean hasArrayValues;
	private SimpleSnmpClient client;
	
	private String snmpSuffix;
	
	private boolean inAutoMode;
	private boolean isFinished;
	
	private List<OidInfo> oidInfo = null;
	
	private List<SnmpValuesListener> valueListeners = null;
	
	public int getCurrentOidItem() {
		return currentOIDItem;
	}
	
	private OidInfo getNextItem() {
		OidInfo res = null;
		if (!isFinished) {
			currentOIDItem++;
			
			while (currentOIDItem < oidInfo.size()) {
				if ((isInit) || (hasArrayValues) || (oidInfo.get(currentOIDItem).isRepeatable())) {
					res = oidInfo.get(currentOIDItem);
					break;
				}
				currentOIDItem++;
			}
		}
		
		return res;
	}
	
	public List<OidInfo> oidInfoList() {
		return oidInfo;
	}
	
	public void gotoStart() {
		currentOIDItem = -1;
	}
	
	private boolean eof() {
		return currentOIDItem >= getOids().size() - 1;
	}
	
	public boolean bof() {
		return currentOIDItem == -1;
	}
	
	public SnmpInfo() {
		
	}
	
	public SnmpInfo(SimpleSnmpClient client, String snmpSuffix) {
		this.client = client;
		valueListeners = new ArrayList<SnmpValuesListener>();
		this.snmpSuffix = snmpSuffix;
	}
	
	public SnmpInfo(SimpleSnmpClient client) {
		this(client, null);
	}
	
	protected void initialize() {
		gotoStart();
		isInit = true;
		
		oidInfo = getOids();
		
		hasArrayValues = false;
		hasRepeatedValues = false;
		for (int i = 0; i < oidInfo.size(); i++) {
			if (oidInfo.get(i).isRepeatable()) {
				hasRepeatedValues = true;
				break;
			}
		}
		
		if (!hasRepeatedValues) {
			for (int i = 0; i < oidInfo.size(); i++) {
				if (oidInfo.get(i).getPduType() == PDU.GETNEXT) {
					hasArrayValues = true;
					break;
				}
			}
		}
		
		inAutoMode = true;
		isFinished = false;
	}
	
	public void addSnmpValueListener(SnmpValuesListener listener) {
		valueListeners.add(listener);
	}
	
	public void removeSnmpValueListener(SnmpValuesListener listener) {
		valueListeners.remove(listener);
	}
	
	private boolean setCurrentValue(PDU response, PDU request) {
		boolean res = false;
		if ((response != null) && (request != null) && (!isFinished)) {
			VariableBinding bdres = response.get(0);
			VariableBinding bdreq = request.get(0);
			if ((bdres != null) && (bdreq != null) && (response.getErrorStatus() == 0) &&
					(bdres.getOid() != null) && (bdres.getOid().size() >= bdreq.getOid().size()) &&
					(bdreq.getOid().leftMostCompare(bdres.getOid().size() - 1, bdres.getOid()) == 0) && 
					(!Null.isExceptionSyntax(bdres.getVariable().getSyntax()))) { // &&
					//(bdres.getOid().compareTo(arg0)))
				if ((currentOIDItem >= 0) && (currentOIDItem < oidInfo.size())) {
					res = true;
					
					oidInfo.get(currentOIDItem).setValue(bdres);
				}
			}
		}
		
		return res;
	}
	
	public void start() {
		gotoStart();
		inAutoMode = true;
		isFinished = false;
		requestNextItem();
	}
	
	public void stop() {
		isFinished = true;
	}
	
	public void getSpecific(int index) {
		if ((index >= 0) && (index < oidInfo.size())) {
			inAutoMode = false;
			currentOIDItem = index - 1;
			isFinished = false;
			requestNextItem();
		}
	}
	
	private boolean requestNextItem() {
		return requestItem(getNextItem());
	}
	
	private boolean requestItem(OidInfo info) {
		boolean res = false;
		if ((info != null) && (!isFinished)) {
			res = true;
			
			String suffix;
			
			if (snmpSuffix == null) {
				suffix = ".0";
			}
			else {
				suffix = "." + snmpSuffix;
			}
			
			VariableBinding[] bd = null;
			if (hasArrayValues) {
				if (info.getPduType() == PDU.GETNEXT) {
					suffix = "";
					if (info.getBinding() != null) {
						bd = new VariableBinding[] { info.getBinding() };
					}
				}
				else {
					suffix = "." + oidInfo.get(0).getValue().toString();
				}
			}
			
			if (!isFinished) {
				System.out.println("Requesting oid: " + info.getOid() + suffix);
				client.get(new OID[] { new OID(info.getOid() + suffix) }, this, info, info.getPduType(), bd);
			}
		}
		
		return res;
	}
	
	@Override
	public void onResponse(ResponseEvent response) {
		
		if (response == null) {
			System.out.println("Error while getting snmp response object");
		}
		else if (response.getError() != null) {
			System.out.println(response.getError().getMessage());
		}
		else if ((response.getUserObject() != null) && (response.getUserObject() instanceof OidInfo) &&
				(response.getResponse() != null) && (!isFinished)) {
			OidInfo info = (OidInfo)response.getUserObject();
			
			boolean isSet = setCurrentValue(response.getResponse(), response.getRequest());
			
			if ((info.getPduType() == PDU.GETNEXT) && (!isSet)) {
				return;
			}
			
			if ((inAutoMode) && (!isFinished)) {
				if (eof()) {
					
					for (SnmpValuesListener listener : valueListeners) {
						if (!isFinished) {
							listener.valuesRetrieved(this);
						}
					}
					
					isInit = false;
					
					gotoStart();
					
					if (hasArrayValues) {
						requestNextItem();
					}
					else if (hasRepeatedValues) {
						Timer timer = new Timer();
						timer.schedule(new TimerTask() {
	
							@Override
							public void run() {
								if (!isFinished) {
									requestNextItem();
								}
							}
							
						}, 1000);
					}
				}
				else if (!isFinished) {
					requestNextItem();
				}
			}
		}
	}
	
	public boolean isInit() {
		return isInit;
	}
	
	protected abstract List<OidInfo> getOids();
}
